package com.lishui.wateroj.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.lishui.wateroj.contant.CommonConstant;
import com.lishui.wateroj.contant.MessageConstant;
import com.lishui.wateroj.exception.BusinessException;
import com.lishui.wateroj.judge.JudgeService;
import com.lishui.wateroj.mapper.QuestionSubmitMapper;
import com.lishui.wateroj.model.context.BaseContext;
import com.lishui.wateroj.model.enumeration.QuestionSubmitLanguageEnum;
import com.lishui.wateroj.model.enumeration.QuestionSubmitStatusEnum;
import com.lishui.wateroj.model.pojo.dto.QuestionSubmitAddDTO;
import com.lishui.wateroj.model.pojo.dto.QuestionSubmitQueryDTO;
import com.lishui.wateroj.model.pojo.entity.Question;
import com.lishui.wateroj.model.pojo.entity.QuestionSubmit;
import com.lishui.wateroj.model.pojo.entity.User;
import com.lishui.wateroj.model.pojo.vo.QuestionSubmitVO;
import com.lishui.wateroj.model.pojo.vo.QuestionVO;
import com.lishui.wateroj.model.pojo.vo.UserVO;
import com.lishui.wateroj.result.PageResult;
import com.lishui.wateroj.service.QuestionService;
import com.lishui.wateroj.service.QuestionSubmitService;
import com.lishui.wateroj.service.UserService;
import com.lishui.wateroj.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author 24197
* @description 针对表【question_submit(题目提交)】的数据库操作Service实现
* @createDate 2024-04-13 21:17:48
*/
@Service
@Slf4j
public class QuestionSubmitServiceImpl extends ServiceImpl<QuestionSubmitMapper, QuestionSubmit>
    implements QuestionSubmitService {
    @Resource
    private QuestionService questionService;

    @Resource
    private UserService userService;

    @Resource
    @Lazy
    private JudgeService judgeService;

    @Resource
    @Lazy
    private QuestionSubmitService questionSubmitService;

    @Resource
    private RedissonClient redissonClient;
    /**
     * 获取查询包装类（用户根据哪些字段查询，根据前端传来的请求对象，得到 mybatis 框架支持的查询 QueryWrapper 类）
     */
    @Override
    public QueryWrapper<QuestionSubmit> getQueryWrapper(QuestionSubmitQueryDTO questionSubmitQueryDTO) {
        QueryWrapper<QuestionSubmit> queryWrapper = new QueryWrapper<>();
        if (questionSubmitQueryDTO == null) {
            return queryWrapper;
        }
        String language = questionSubmitQueryDTO.getLanguage();
        Integer status = questionSubmitQueryDTO.getStatus();
        Long questionId = questionSubmitQueryDTO.getQuestionId();
        Long userId = questionSubmitQueryDTO.getUserId();
        String sortField = questionSubmitQueryDTO.getSortField();
        String sortOrder = questionSubmitQueryDTO.getSortOrder();

        // 拼接查询条件
        queryWrapper.eq(StringUtils.isNotBlank(language), "language", language);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "user_id", userId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(questionId), "question_id", questionId);
        queryWrapper.eq(QuestionSubmitStatusEnum.getEnumByValue(status) != null, "status", status);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    @Override
    public QuestionSubmitVO getQuestionSubmitVO(QuestionSubmit questionSubmit) {
        QuestionSubmitVO questionSubmitVO = QuestionSubmitVO.objToVo(questionSubmit);
        long userId = BaseContext.getCurrentId();
        // 获取userVO 和 QuestionVO
        User user = userService.getById(questionSubmit.getUserId());
        Question question = questionService.getById(questionSubmit.getQuestionId());
        UserVO userVO = new UserVO();
        QuestionVO questionVO = new QuestionVO();
        BeanUtils.copyProperties(user, userVO);
        BeanUtils.copyProperties(question, questionVO);
        questionSubmitVO.setUserVO(userVO);
        questionSubmitVO.setQuestionVO(questionVO);
        // 处理脱敏
        if (userId != questionSubmit.getUserId() && !userService.isAdmin()) {
            questionSubmitVO.setCode(null);
        }
        return questionSubmitVO;
    }

    @Override
    public Long doQuestionSubmit(QuestionSubmitAddDTO questionSubmitAddDTO) {
        // 校验编程语言是否合法
        String language = questionSubmitAddDTO.getLanguage();
        QuestionSubmitLanguageEnum languageEnum = QuestionSubmitLanguageEnum.getEnumByValue(language);
        if (languageEnum == null) {
            throw new BusinessException("编程语言错误");
        }
        long questionId = questionSubmitAddDTO.getQuestionId();
        // 判断实体是否存在，根据类别获取实体
        Question question = questionService.getById(questionId);
        if (question == null) {
            throw new BusinessException(MessageConstant.NO_EXISTS + ":question");
        }
        long userId = BaseContext.getCurrentId();
        Long questionSubmitId = null;
        // 该用户在该题还在判题或等待中时，不能提交重复的题目。高并发时，会出现线程问题。
        boolean flag = false;
        final String QUESTION_SUBMIT_LOCK = "wateroj:question-submit:lock:%d:%d";
        RLock lock = redissonClient.getLock(String.format(QUESTION_SUBMIT_LOCK, userId, questionId));
        try {
            if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
                QueryWrapper<QuestionSubmit> questionSubmitQueryWrapper = new QueryWrapper<>();
                questionSubmitQueryWrapper.eq("user_id", userId);
                questionSubmitQueryWrapper.eq("question_id", questionId);
                questionSubmitQueryWrapper.in("status", QuestionSubmitStatusEnum.WAITING, QuestionSubmitStatusEnum.RUNNING);
                long count = questionSubmitService.count(questionSubmitQueryWrapper);
                if (count == 0) {
                    // 每个用户串行提交题目
                    QuestionSubmit questionSubmit = new QuestionSubmit();
                    questionSubmit.setUserId(userId);
                    questionSubmit.setQuestionId(questionId);
                    questionSubmit.setCode(questionSubmitAddDTO.getCode());
                    questionSubmit.setLanguage(language);
                    // 设置初始状态
                    questionSubmit.setStatus(QuestionSubmitStatusEnum.WAITING.getValue());
                    questionSubmit.setJudgeInfo("{}");
                    boolean save = this.save(questionSubmit);
                    if (!save){
                        throw new BusinessException(MessageConstant.SAVE_EXCEPTION);
                    }
                    questionSubmitId = questionSubmit.getId();
                    final long finalQuestionSubmitId = questionSubmitId;
                    // 执行判题服务
                    CompletableFuture.runAsync(() -> {
                        judgeService.doJudge(finalQuestionSubmitId);
                    });
                } else {
                    throw new BusinessException(MessageConstant.ALREADY_EXISTS + ":正在执行的题目记录");
                }
            } else {
                throw new BusinessException(MessageConstant.ALREADY_EXISTS + ":正在执行的题目记录");
            }
        } catch (Exception e) {
            flag = true;
        } finally {
            // 最后释放自己的锁
            if (lock.isHeldByCurrentThread()) {
                log.info("unLock: " + Thread.currentThread().getId());
                lock.unlock();
            }
        }
        if (flag) throw new BusinessException(MessageConstant.ALREADY_EXISTS + ":正在执行的题目记录");
        return questionSubmitId;
    }

    @Override
    public PageResult getQuestionSubmitVOPage(QuestionSubmitQueryDTO questionSubmitQueryDTO) {
        Integer pageNum = questionSubmitQueryDTO.getPageNum();
        Integer pageSize = questionSubmitQueryDTO.getPageSize();
        QueryWrapper<QuestionSubmit> queryWrapper = getQueryWrapper(questionSubmitQueryDTO);
        PageHelper.startPage(pageNum, pageSize);
        List<QuestionSubmit> questionSubmitList = this.list(queryWrapper);
        PageInfo<QuestionSubmit> pageInfo = new PageInfo<>(questionSubmitList);
        List<QuestionSubmitVO> questionSubmitVOList = questionSubmitList.stream()
                .map(this::getQuestionSubmitVO)
                .toList();
        return new PageResult(pageInfo.getTotal(), questionSubmitVOList);
    }

}




